#include <iostream>
#include <stdlib.h>
#include <time.h>
#include <math.h>
#include "environment.h"
#include "animal.h"
#include <iomanip>

using namespace std;

int main () {

	// Randomizing number generator
	srand( time( NULL ) );

	int menuSelect, timer, predatorsNumber = 1;

	cout << setprecision(4) << fixed;

	// Loading environment
	Environment env;

	// Creating predators and Preys
	Animal * animals[5] = { new Animal(&env), new Animal(&env), new Animal(&env), new Animal(&env), new Animal(&env) };

	// Loading actors into the environment
	env.addPrey(animals[0], 5, 5);
	env.addPredator(animals[1], 0, 0);

	// Printing introduction
	cout << "\n\nAutonomous Agents Lab - Assignment 3\n";
	cout << "Selection menu:\n\n";

	do {

	// Printing Menu
	cout << "Enter 1 to execute the simulation of the environment;\n";
	cout << "Enter 2 to print the animals' current parameters;\n";
	cout << "Enter 3 to change the animals' parameters ( will reset learning );\n";
	cout << "Enter 4 to restart the animals' learning;\n";
	cout << "Enter 5 to change the animals' algorithm;\n";
	cout << "Enter 6 to change the number of predators;\n";
	cout << "Enter 0 to exit;\n\n";
	cin >> menuSelect;
	cout << "\n";

	timer = clock();

	switch ( menuSelect ) {

		// SIMULATION WITH SET PARAMETERS
		case 1: {
			int selection, episodes;
			env.setPrint(false);

			do {
				cout << "Enter 1 to execute a single episode and to print all moves;\n";
				cout << "Enter 2 to execute 100 episodes;\n";
				cout << "Enter 3 to execute 1000 episodes;\n";
				cout << "Enter 4 to execute k episodes;\n\n";
				cin >> selection;
				cout << "\n";
				switch ( selection ) {
					case 1:
						// Set the simulation printing on
						env.setPrint(true);
						episodes = 1;
						break;
					case 2:
						episodes = 100;
						break;
					case 3:
						episodes = 1000;
						break;
					case 4:
						cout << "Enter k:\n";
						cin >> episodes;
						break;
					default:
						cout << "Invalid selection, please enter one of the following:\n\n";
				}
			} while ( selection < 1 || selection > 4 );

			cout << "Starting Simulation(s)...\n\n";

			// Reset starting timer because we had to wait for input
			timer = clock();

			for ( int i = 0; i < episodes; i++ ) {
				env.startSimulation();
				if ( ! ((i+1) % 100) ) cout << "Made "<<i+1<<" runs!\n";
			}
		}	// End of simulation
		break;

		// PRINT PARAMETERS
		case 2:
			Animal::printParameters();
			cout << "The number of predators is: " << predatorsNumber << "\n";
		break;

		// PARAMETER CHANGE
		case 3: {
			Animal::setParameters();
			cout << "\n";
		}
		// We pass through to case 4 to forget learning

		// ANIMALS FORGET LEARNING
		case 4: {
			if ( Animal::getAlgorithm() != 0 ) {
			double input;
			cout << "Insert new value for Q initialization:\n";
			cin >> input;

			for ( int i = 0; i < 5; i++ )
				animals[i]->resetLearning(input);
			}
			else cout << "Random policy doesn't need to reset learning!\n";
		}
		break;

		// ALGORITHM CHANGE
		case 5: {
			int selection;

			do {
			cout << "Enter 0 for random policy;\n";
			cout << "Enter 1 for Epsilon-greedy Q-learning;\n";
			cout << "Enter 2 for Minimax Q-learning;\n";
			cout << "Enter 3 for WoLF Policy Hill Climbing;\n\n";
			cin >> selection;

			if ( selection < 0 || selection > 3 )
				cout << "Invalid selection, please enter one of the following:\n\n";

			} while ( selection < 0 || selection > 3 );

			if ( selection == 2 && predatorsNumber > 1 ) {
				cout << "Too many predators for this algorithm, please set only one predator before choosing this algorithm!\n";
				break;
			}			

			Animal::setAlgorithm(selection);
			for ( int i = 0; i < 5; i++ ) {
				try {
					animals[i]->init();
				} catch ( bad_alloc& ba ) {
					cout << "\nToo much memory requested! Reverting to Random policy...\n";
					Animal::setAlgorithm(0);
					break;
				}	
			}
		}
		break;

		// CHANGE NUMBER OF PREDATORS
		case 6: {
			do {
			cout << "Enter the number of predators you want;\n";
			cin >> predatorsNumber;

			if ( predatorsNumber < 0 || predatorsNumber > 4 )
				cout << "That number exceeds out parameter limits! Please enter an integer between 1 and 4:\n\n";

			} while ( predatorsNumber < 0 || predatorsNumber > 4 );
		
			env.clearWrapperLists();
			Animal::setNumberOfAgents(0);

			env.addPrey(animals[0], 5, 5);
			
			int temp[4][2] = {{0,0},{10,10},{0,10},{10,0}};
			
			for ( int i = 1; i <= predatorsNumber; i++ ) {
				env.addPredator(animals[i], temp[i-1][0], temp[i-1][1] );		
			}
			for ( int i = 0; i < 5; i++ ) {
				try {
					animals[i]->init();
				} catch ( bad_alloc& ba ) {
					cout << "\nToo much memory requested! Reverting to Random policy...\n";
					Animal::setAlgorithm(0);
					break;
				}
			}
		}			
		break;

		// EXIT PROGRAM
		case 0:
			cout << "Goodbye!\n\n\n";
			continue;

		default:
			cout << "Invalid selection, please enter one of the following:\n\n";
			continue;

        }	// Switch menuSelect

	timer = clock() - timer;
	cout << "\nThis operation took " << ((float)timer)/CLOCKS_PER_SEC << " seconds.\n\n\n";
    } while (menuSelect != 0);

	env.deleteAllAgents();
	return 0;
}

